GtkStack: Improve focus handling
authorMatthias Clasen <mclasen@redhat.com>
Thu, 11 Dec 2014 03:32:45 +0000 (22:32 -0500)
committerMatthias Clasen <mclasen@redhat.com>
Thu, 11 Dec 2014 03:32:45 +0000 (22:32 -0500)
Add notebook-like focus handling: Keep track of the last focused
descendent of each page, and focus it again when switching back
to the page. If there is no last focused child, we move the focus
into the page as if the user had hit Tab.

gtk/gtkstack.c

index 9726beddb4bd70f2ce7582ac46fc7206ac76433d..b880c6473da4da8a97f91e3299e6ecbc19cc4184 100644 (file)
@@ -112,6 +112,7 @@ struct _GtkStackChildInfo {
   gchar *title;
   gchar *icon_name;
   gboolean needs_attention;
+  GtkWidget *last_focus;
 };
 
 typedef struct {
@@ -996,6 +997,9 @@ set_visible_child (GtkStack               *stack,
   GtkStackChildInfo *info;
   GtkWidget *widget = GTK_WIDGET (stack);
   GList *l;
+  GtkWidget *toplevel;
+  GtkWidget *focus;
+  gboolean contains_focus = FALSE;
 
   /* If none, pick first visible */
   if (child_info == NULL)
@@ -1014,6 +1018,25 @@ set_visible_child (GtkStack               *stack,
   if (child_info == priv->visible_child)
     return;
 
+  toplevel = gtk_widget_get_toplevel (widget);
+  if (GTK_IS_WINDOW (toplevel))
+    {
+      focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
+      if (focus &&
+          priv->visible_child &&
+          gtk_widget_is_ancestor (focus, priv->visible_child->widget))
+        {
+          contains_focus = TRUE;
+
+          if (priv->visible_child->last_focus)
+            g_object_remove_weak_pointer (G_OBJECT (priv->visible_child->last_focus),
+                                          (gpointer *)&priv->visible_child->last_focus);
+          priv->visible_child->last_focus = focus;
+          g_object_add_weak_pointer (G_OBJECT (priv->visible_child->last_focus),
+                                     (gpointer *)&priv->visible_child->last_focus);
+        }
+    }
+
   if (priv->last_visible_child)
     gtk_widget_set_child_visible (priv->last_visible_child->widget, FALSE);
   priv->last_visible_child = NULL;
@@ -1033,7 +1056,17 @@ set_visible_child (GtkStack               *stack,
   priv->visible_child = child_info;
 
   if (child_info)
-    gtk_widget_set_child_visible (child_info->widget, TRUE);
+    {
+      gtk_widget_set_child_visible (child_info->widget, TRUE);
+
+      if (contains_focus)
+        {
+          if (child_info->last_focus)
+            gtk_widget_grab_focus (child_info->last_focus);
+          else
+            gtk_widget_child_focus (child_info->widget, GTK_DIR_TAB_FORWARD);
+        }
+    }
 
   if ((child_info == NULL || priv->last_visible_child == NULL) &&
       is_direction_dependent_transition (transition_type))
@@ -1164,6 +1197,7 @@ gtk_stack_add (GtkContainer *container,
   child_info->title = NULL;
   child_info->icon_name = NULL;
   child_info->needs_attention = FALSE;
+  child_info->last_focus = NULL;
 
   priv->children = g_list_append (priv->children, child_info);
 
@@ -1224,6 +1258,11 @@ gtk_stack_remove (GtkContainer *container,
   g_free (child_info->name);
   g_free (child_info->title);
   g_free (child_info->icon_name);
+
+  if (child_info->last_focus)
+    g_object_remove_weak_pointer (G_OBJECT (child_info->last_focus),
+                                  (gpointer *)&child_info->last_focus);
+
   g_slice_free (GtkStackChildInfo, child_info);
 
   if ((priv->hhomogeneous || priv->vhomogeneous) && was_visible)